package com.sunxiansheng.redis.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Component
public class RedisShareLockUtil {

    private static final Logger logger = LoggerFactory.getLogger(RedisShareLockUtil.class);
    private static final long DEFAULT_TIMEOUT = 10; // 默认超时时间（秒）
    private static final String DEFAULT_LOCK_NAME_PREFIX = "lock:"; // 默认锁名前缀

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 尝试获取锁
     * @param lockKey 锁的key
     * @param timeout 超时时间（秒）
     * @return 锁的value，用于释放锁
     */
    public String tryLock(String lockKey, long timeout) {
        String value = UUID.randomUUID().toString();
        Boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, value, timeout, TimeUnit.SECONDS);
        while (locked == null || !locked) {
            if (locked == null) {
                logger.error("尝试获取锁时出现错误，可能是 Redis 连接问题，key: {}", lockKey);
            } else {
                logger.warn("获取锁失败，重试中，key: {}", lockKey);
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                logger.error("尝试获取锁时线程被中断，key: {}", lockKey, e);
                return null;
            }
            locked = redisTemplate.opsForValue().setIfAbsent(lockKey, value, timeout, TimeUnit.SECONDS);
        }
        logger.info("成功获取锁，key: {}", lockKey);
        return value;
    }

    /**
     * 释放锁
     * @param lockKey 锁的key
     * @param value 锁的value
     */
    public boolean releaseLock(String lockKey, String value) {
        String scriptText = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        DefaultRedisScript<Long> script = new DefaultRedisScript<>();
        script.setScriptText(scriptText);
        script.setResultType(Long.class);
        Long result = redisTemplate.execute(script, Collections.singletonList(lockKey), value);
        if (result != null && result > 0) {
            logger.info("成功释放锁，key: {}", lockKey);
            return true;
        } else {
            if (result == null) {
                logger.error("释放锁失败，Redis 执行错误，key: {}", lockKey);
            } else {
                logger.warn("释放锁失败，锁不存在或锁的值不匹配，key: {}", lockKey);
            }
            return false;
        }
    }


    /**
     * 使用锁执行操作，使用默认超时时间
     * @param lockKey 锁的key
     * @param action 要执行的操作
     */
    public void executeWithLock(String lockKey, Runnable action) {
        executeWithLock(lockKey, DEFAULT_TIMEOUT, action);
    }

    /**
     * 使用锁执行操作
     * @param lockKey 锁的key
     * @param timeout 超时时间（秒）
     * @param action 要执行的操作
     */
    public void executeWithLock(String lockKey, long timeout, Runnable action) {
        String fullLockKey = DEFAULT_LOCK_NAME_PREFIX + lockKey;
        String value = tryLock(fullLockKey, timeout);
        if (value != null) {
            try {
                action.run();
            } finally {
                releaseLock(fullLockKey, value);
            }
        } else {
            logger.warn("无法获取锁，key: {}", fullLockKey);
        }
    }
}